﻿#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//lambda 表达式本质是⼀个匿名函数对象，跟普通函数不同的是他可以定义在函数内部。
//lambda 表达式语法使⽤层⽽⾔没有类型，所以我们⼀般是⽤auto或者模板参数定义的对象去接
//收 lambda 对象。

//lambda表达式的格式：[capture - list](parameters)-> return type{
//function boby }

void  test1() {  //几个简单的lambda表达式
	auto add1 = [](int x, int y)->int {
		return x + y;
		};
	// 1、捕捉为空也不能省略
    // 2、参数为空可以省略
    // 3、返回值可以省略，可以通过返回对象⾃动推导
    // 4、函数体不能省略
	cout << add1(1, 2) << endl;

	auto print = [] {
		cout << "hello world!" << endl;
		};
	print();

	int a = 0, b = 1;
	auto swap = [](int& x, int& y) {
		int tmp = x;
		x = y;
		y = tmp;
		};
	swap(a, b);
	cout << a << "  " << b << endl;

}


int all = 10;
void test2() { //捕捉列表
	//1.lambda 表达式中默认只能⽤ lambda 函数体和参数中的变量，如果想⽤外层作⽤域中的变量就
	//需要进⾏捕捉

	//2.第⼀种捕捉⽅式是在捕捉列表中显⽰的传值捕捉和传引⽤捕捉，捕捉的多个变量⽤逗号分割。[x，
	// y，& z] 表⽰x和y值捕捉，z引⽤捕捉。

	int x = 1, y = 2, z = 3;
	auto func = [x, y, &z]() {

		//x++; //值捕捉的变量不能修改，引用捕捉的变量可以
		//y++;
		z = 9;    //其可以改变外部的z，毕竟是引用  
		return x + y + z;
		};
	cout << func() << endl;
	cout << x << "  " << y << "  " << z << endl;

	//3.隐式值捕捉
	//当前函数作用域用的变量全部捕捉

	auto func2 = [=] {
		return x + y + z;
		};

	cout << func2() << endl;

	// 4.隐式引⽤捕捉
	//当前函数作用域用的变量全部引用捕捉

	auto func3 = [&] {
		x = 4, y = 5, z = 6;
		return x + y + z;
		};

	cout << func3() << endl;


	//5.混合捕捉
	auto func4 = [=, &z] {//除了z其他都是值捕捉
		z++;
		return x + y + z;
		};
	cout << func4() << endl;
	auto func5 = [&, y] { //除了y其他都是引用捕捉
		x++;
		z++;
		return x + y + z;
		};
	cout << func5() << endl;

	//6. 局部的静态和全局变量不能捕捉，
	// 也不需要捕捉,它们都可以修改
	static int p = 4;
	auto func6 = [] {
		all = 0;
		p = 1;
		return all + p;
		};
	cout << func6() << endl;

}

struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 评价
	// ...
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};

//仿函数：升序
struct ComparePriceLess
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price < gr._price;
	}
};
//降序
struct ComparePriceGreater
{
	bool operator()(const Goods& gl, const Goods& gr)
	{
		return gl._price > gr._price;
	}
};


void test3() { //lambda的应用
	//在学习 lambda 表达式之前，我们的使⽤的可调⽤对象只有函数指针和仿函数对象，函数指针的
	//类型定义起来⽐较⿇烦，仿函数要定义⼀个类，相对会⽐较⿇烦。使⽤ lambda 去定义可调⽤对
	//象，既简单⼜⽅便。

	vector<Goods> v({ {"苹果",2.2,4},{"香蕉",4.2,6},{"西瓜",1.2,2} });
    
	// 类似这样的场景，我们实现仿函数对象或者函数指针⽀持商品中
    // 不同项的⽐较，相对还是⽐较⿇烦的，那么这⾥lambda就很好⽤了
	
	
	sort(v.begin(), v.end(), ComparePriceLess()); //sort的第三个参数可以传一个仿函数对象
	sort(v.begin(), v.end(), ComparePriceGreater());

	//按价格升序：
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		             return  g1._price < g2._price;
		});   //第三个参数传了lambda匿名函数对象
	//按价格降序：
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return  g1._price > g2._price;
		});
	//按评价升序：
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return  g1._evaluate < g2._evaluate;
		});
	//按评价降序：
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return  g1._evaluate > g2._evaluate;
		});

}

void test4() { //lambda的原理

	//1.lambda 的原理和范围for很像，编译后从汇编指令层的⻆度看，压根就没有 lambda 和范围for
	//  这样的东西。范围for底层是迭代器，⽽lambda底层是仿函数对象，也就说我们写了⼀个
	//  lambda 以后，编译器会⽣成⼀个对应的仿函数的类。

	//2.该仿函数的类名是编译按⼀定规则⽣成的，保证不同的 lambda ⽣成的类名不同，lambda参数 / 返
	//  回类型 / 函数体就是仿函数operator()的参数 / 返回类型 / 函数体， lambda 的捕捉列表本质是⽣成
	//	的仿函数类的成员变量，
	//也就是说捕捉列表的变量都是 lambda 类构造函数的实参，当然隐式捕
	//	捉，编译器要看使⽤哪些就传那些对象，这也是编译器的灵活之处

}





//仿函数：
struct Rate
{
	Rate(double rate)
		: _rate(rate)
	{}
	double operator()(double money, int year)
	{
		return money * _rate * year;
	}
private:
	double _rate;
};
void test5() {  //从底层汇编角度理解lambda的本质

	double rate = 0.2;

	//这也是为甚么要用auto来推导出lambda的类型名，因为该类型名是编译器自动生成的
    //无法知道
	auto my_lambda = [rate](double money, int year) {
		return money * rate * year;
		};
	
    //此时，my_lambda就可以看成普通的仿函数对象


	Rate r1(0.4); //仿函数对象
	r1(10000, 4);  //调用operator()

	my_lambda(20000, 2);

}
int main() {
	test5();


	return 0;
}